package org.song.filter;

import cn.hutool.core.lang.Assert;
import org.song.cache.UserInfoTokenCache;
import org.song.exception.myException.CommonException;
import org.song.util.JwtTokenUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Objects;

/**
 * @author song
 * @date 2022/3/9 17:53
 * 自定义前置过滤器判断用户是否携带token 以及给 token 延时，每次请求都会经过该过滤器    1
 *
 **/

@Service( value = "myOncePerRequestFilter")
public class MyOncePerRequestFilter extends OncePerRequestFilter {

    // HTTP认证请求头KEY
    public static final String AUTHORIZATION_HEAD = "Authorization";

    @Autowired( required = true )
    @Qualifier( value = "myUserDetailsServiceImpl")
    private UserDetailsService userDetailsService;

    @Autowired( required = true )
    @Qualifier( value = "jwtTokenUtil")
    private JwtTokenUtil jwtTokenUtil;

    @Autowired( required = true )
    @Qualifier( value = "userInfoTokenCacheImpl")
    private UserInfoTokenCache userInfoTokenCache;

    /**
     * 开始执行内部过滤器链
     * @param request
     * @param response
     * @param filterChain
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 获取http认证请求头请求头
        final String requestHeader = request.getHeader(AUTHORIZATION_HEAD);
        if ( !StringUtils.isEmpty( requestHeader ) && !Objects.equals( "null" , requestHeader )) {
            // 请求头不为空用户已经登录 取出token
            final String token = requestHeader.replace("Bearer", "").trim();
            // 解析token取出用户Id 进行延时（重置过期时间，或者使用lua脚本延时 redission看门狗延时操作）防止用户再操作 token 缓存中过期
            // token 未过期 ， 通过本次请求携带的 token 来获取 用户名
            final String username = jwtTokenUtil.getUsernameFromToken(token);
            // 判断token是否过期
            Assert.isTrue( !StringUtils.isEmpty( userInfoTokenCache.getOnlineUser( username ) ) , () -> new CommonException("身份已经过期，请重新登录"));
            // 通过用户名获取用户信息
            final UserDetails userDetails = userDetailsService.loadUserByUsername(username);
            // token未过期，重置用户token时间 生成新token默认为7天 ， key 重复value覆盖
            userInfoTokenCache.setExOnlineUser( username , jwtTokenUtil.generateToken( userDetails ) );   //TODO; 使用lua脚本代替延时
            // 根据当前用户信息生成新的springSecurity的自带token实例
            UsernamePasswordAuthenticationToken springSecurityToken = new UsernamePasswordAuthenticationToken(
                    userDetails,
                    null,
                    userDetails.getAuthorities()
            );
            // 请求实例存入SpringSecurityWEB认证
            springSecurityToken.setDetails( new WebAuthenticationDetailsSource().buildDetails( request ) );
            // ！！！ 重点 SpringSecurityContextHolder容器 是一个 ThreadLocal 保证多线程并发安安全
            SecurityContextHolder.getContext().setAuthentication( springSecurityToken );
        }
        //用户未登录直接放行 , 将 request response 实例传给下一个调用链 再整个springSecurity递归调用链中都是一个请求和响应实例
        filterChain.doFilter( request , response );

    }

}
